/*
Copyright 2008, 2009 Joonas Lehtolahti

This file is part of Openfire-C.

Openfire-C is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Openfire-C is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Openfire-C.  If not, see <http://www.gnu.org/licenses/>.
*/

int XfireAnalyze_hFavorites(XFINST inst, struct XfirePacket *packet) {
	/* List of favorite servers
	 * http://xfireplus.com/xfwiki/index.php/Packet_94_00
	 *
	 * SINGLE LIST CALLBACKS
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, LinkedList*
	#define _XFABP *max_value, server_list
	
	unsigned int *max_value = XfirePacketGetIntegerAttribute(packet, (unsigned const char *)"max");
	LinkedList *gameid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gameid");
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gip");
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gport");
	
	if (max_value && gameid_list && gameip_list && gameport_list) {
		if (gameid_list->nodes == gameip_list->nodes && gameip_list->nodes == gameport_list->nodes) {
			
			LinkedList *server_list = LLcreate();
			
			ListNode *gameid_node = gameid_list->first;
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			while (gameid_node) {
				LLaddLast(server_list, XfireGameData(*(unsigned int*)gameid_node->x,
					*(unsigned int*)gameip_node->x, *(unsigned int*)gameport_node->x));
				
				NEXT_NODE(gameid_node);
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				struct XfFavoriteServers *favs = inst->fav_servers;
				if (favs) {
					LLdelete(favs->servers, (FREEFUNC*)FreeXfireGameData);
					favs->servers = server_list;
					favs->maxfavorites = *max_value;
				}
				else {
					/* Favorite servers structure not found?! */
					retval |= XFIH_NONFATAL;
				}
			}
			else {
				/* Do not store favorite servers */
				LLdelete(server_list, (FREEFUNC*)FreeXfireGameData);
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(gameid_list, NULL);
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	
	return retval;
}

int XfireAnalyze_hFriendFav(XFINST inst, struct XfirePacket *packet) {
	/* List of friends' favorite servers for specific game
	 * http://xfireplus.com/xfwiki/index.php/Packet_95_00
	 *
	 * USES SINGLE-LIST CALLBACK WAY!
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, LinkedList*
	#define _XFABP *gameid_value, favs_list
	
	unsigned int *gameid_value = XfirePacketGetIntegerAttribute(packet, (unsigned const char *)"gameid");
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gip");
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gport");
	LinkedList *friends_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"friends");
	
	if (gameid_value && gameip_list && gameport_list && friends_list) {
		if (friends_list->nodes == gameip_list->nodes && gameip_list->nodes == gameport_list->nodes) {
			/* Need to convert the friends_list from packet data to actual friends */
			ListNode *friends_node = friends_list->first;
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			
			LinkedList *favs_list = LLcreate();
			
			struct XfirePattr4 *alist;
			unsigned int userid;
			struct XfFriend *xff;
			while(friends_node) {
				unsigned int i = 0;
				LinkedList *thislist = LLcreate();
				struct XfGameData *thisgame = XfireGameData(*gameid_value,
					*(unsigned int*)gameip_node->x, *(unsigned int*)gameport_node->x);
				
				alist = (struct XfirePattr4*)friends_node->x;
				while (i<alist->size) {
					userid = *(unsigned int*)(alist->attr[i++]);
					xff = XfireInst_FindUserByUserid(inst, userid);
					if (xff) {
						LLaddLast(thislist, xff);
					}
					else {
						/* A favorite server of a friend but there is no such friend? */
						retval |= XFIH_NONFATAL;
					}
				}
				thisgame->extdata->byfriends = thislist;
				
				LLaddLast(favs_list, thisgame);
				
				NEXT_NODE(friends_node);
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				struct XfGameServerList cmplist, *xfgsl;
				TreeNode *oldlist;
				cmplist.gameid = *gameid_value;
				oldlist = BTreeFind(inst->friends_favs, &cmplist);
				if (oldlist) {
					xfgsl = (struct XfGameServerList *)oldlist->x;
					LLmerge(xfgsl->servers, favs_list);
				}
				else {
					xfgsl = XfireGameServerList(*gameid_value, favs_list);
					BTreeAdd(inst->friends_favs, xfgsl);
				}
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(friends_list, NULL);
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	
	return retval;
}

int XfireAnalyze_hServers(XFINST inst, struct XfirePacket *packet) {
	/* List of all servers for specific game
	 * http://xfireplus.com/xfwiki/index.php/Packet_96_00
	 *
	 * USES SINGLE-LIST CALLBACK WAY!
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, LinkedList*
	#define _XFABP *gameid_value, server_list
	
	unsigned int *gameid_value = XfirePacketGetIntegerAttribute(packet, (unsigned const char *)"gameid");
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gip");
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gport");
	
	if (gameid_value && gameip_list && gameport_list) {
		if (gameip_list->nodes == gameport_list->nodes) {
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			
			LinkedList *server_list = LLcreate();
			
			while(gameip_node) {
				struct XfGameData *thisgame = XfireGameData(*gameid_value,
					*(unsigned int*)gameip_node->x, *(unsigned int*)gameport_node->x);
				
				LLaddLast(server_list, thisgame);
				
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				struct XfGameServerList cmplist, *xfgsl;
				TreeNode *oldlist;
				cmplist.gameid = *gameid_value;
				oldlist = BTreeFind(inst->game_servers, &cmplist);
				if (oldlist) {
					xfgsl = (struct XfGameServerList *)oldlist->x;
					LLmerge(xfgsl->servers, server_list);
				}
				else {
					xfgsl = XfireGameServerList(*gameid_value, server_list);
					BTreeAdd(inst->game_servers, xfgsl);
				}
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	
	return retval;
}
